#include <src/pcps.h>
#include <src/random.h>

//--------------------------------------------------------------------------------
// pcps::global_lcg -- Initialization for the global linear congruential random
//                     number generator.
//
//                     The use of m = 2^31 - 1, a = 16807, c = 0 is recommended by
//
//                     S. K. Park and K. W. Miller,
//                     ``Random Number Generators: Good Ones are Hard to Find,''
//                     Transactions of the ACM, Nov. 1988.
//--------------------------------------------------------------------------------

pcps::LinearCongruentialGenerator pcps::global_lcg(pcps::pow<pcps::lcg_int_t>(2,31)-1, 16807, 0, 1);

//-------------------------------------------------------------------------------
// pcps::set_seed -- sets the random seed on each process
//-------------------------------------------------------------------------------

void pcps::set_seed(int seed) {

  // get MPI info
  const MPI::Comm & comm = MPI::COMM_WORLD;
  const int nproc = comm.Get_size();
  const int myrank = comm.Get_rank();

  pthread_mutex_lock(pcps::rand_mutex());

  // temporarily initialize using the provided seed
  pcps::global_lcg.set_seed(seed);

  // produce a different seed for each process
  std::vector<unsigned int> seeds(nproc);
  for (int i = 0; i < nproc; i++) {
    unsigned int temp;
    for (int j = 0; j < 1000000; j++) temp = pcps::global_lcg(); // separate the seeds by a million random numbers
    seeds.at(i) = pcps::global_lcg();
  }

  pthread_mutex_unlock(pcps::rand_mutex());

  // give every process a different seed
  comm.Scatter(&seeds.at(0), 1, MPI::UNSIGNED, &seed, 1, MPI::UNSIGNED, 0);

  // initialize using this process's seed
  pthread_mutex_lock(pcps::rand_mutex());
  pcps::global_lcg.set_seed(seed);
  pthread_mutex_unlock(pcps::rand_mutex());

}
